home *** CD-ROM | disk | FTP | other *** search
/ MacAddict 114 / macaddict114.cdr / Software / Utilities / macam.0.8.4.dmg / macam sources / app_specific / MyController.m < prev    next >
Encoding:
Text File  |  2005-11-16  |  53.1 KB  |  1,480 lines

  1. /*
  2.  MyController.m - Controller for camera window
  3.  
  4.     Copyright (C) 2002 Matthias Krauss (macam@matthias-krauss.de)
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  $Id: MyController.m,v 1.20 2005/11/16 18:25:00 hxr Exp $
  20. */
  21.  
  22. #import "MyController.h"
  23. #import "MyCameraInspector.h"
  24. #import "MiscTools.h"
  25. #import "MyMovieRecorder.h"
  26. #import "MyImageDocument.h"
  27.  
  28. #import "BayerConverter.h"
  29. #import "RGBScaler.h"
  30.  
  31. static NSString*     ControllerToolbarIdentifier    = @"Controller Toolbar Identifier";
  32. static NSString*     PlayToolbarItemIdentifier    = @"Play Video Item Identifier";
  33. static NSString*    SettingsToolbarItemIdentifier     = @"Camera Settings Item Identifier";
  34. static NSString*    DownloadToolbarItemIdentifier     = @"Download Media Item Identifier";
  35. static NSString*    SaveImageToolbarItemIdentifier     = @"Save Image Item Identifier";
  36. static NSString*    NextCamToolbarItemIdentifier     = @"Next Camera Item Identifier";
  37. static NSString*    RecordMovieToolbarItemIdentifier= @"Record Movie Item Identifier";
  38.  
  39. extern NSString* MovieSampleDurationPrefsKey;
  40. extern NSString* MoviePlaybackFactorPrefsKey;
  41. extern NSString* MovieTimeTypePrefsKey;
  42. extern NSString* MovieSavePathPrefsKey;
  43. extern NSString* MovieCompressionPrefsKey;
  44. extern NSString* MovieQualityPrefsKey;
  45. extern NSString* SnapshotFormatPrefsKey;
  46. extern NSString* SnapshotQualityPrefsKey;
  47.  
  48. @interface MyController (Private)
  49.  
  50. - (void) startMovieRecording;
  51. - (void) stopMovieRecording;
  52.  
  53. - (BOOL) canDoGrab;
  54. - (BOOL) canToggleSettings;
  55. - (BOOL) canDoDownloadMedia;
  56. - (BOOL) canDoSaveImage;
  57. - (BOOL) canDoNextCam;
  58. - (BOOL) canDoDeleteAll;
  59. - (BOOL) canDoDeleteOne;
  60. - (BOOL) canDoDeleteLast;
  61. - (BOOL) canDoTakeStillImage;
  62. - (BOOL) canDoSavePrefs;
  63. - (BOOL) canDoRecordMovie;
  64. - (void) updateCameraMediaCount;
  65.  
  66. - (BOOL) makeFSSpec:(FSSpec *) specPtr fromPath:(NSString *) inPath;
  67. - (BOOL) addFrameToVideoTrack:(Media) videoTrackMedia withImage:(NSBitmapImageRep *) theImage;
  68. - (BOOL) addImageToMedia:(Media) videoTrackMedia imageData:(NSData *) data of:(NSString *) type;
  69. - (BOOL) addVideoTrack:(Movie) theMovie withContent:(NSArray *) media of:(NSString *) type;
  70. - (BOOL) saveClip:(NSArray *) media ofType:(NSString *) type toFile:(NSString *) filename with:(NSDictionary *) attributes;
  71.  
  72. @end
  73.  
  74. @implementation MyController
  75.  
  76. - (void) awakeFromNib {
  77.     NSString* disclaimerOKVersion;
  78.     NSString* shortVersion;
  79.     NSDictionary* dict;
  80.     
  81.     //Get our short version string
  82.     if ([[NSBundle mainBundle] respondsToSelector:@selector(objectForInfoDictionaryKey:)]) {
  83.         shortVersion=[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
  84.     } else {
  85.         shortVersion=@"<Unknown: Probably Pre-Jaguar System>";
  86.     }
  87.     
  88.     //Create a fallback preference if no version was ever used
  89.     dict=[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.0f] forKey:@"DisclaimerOKVersion"];
  90.     [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
  91.  
  92.     //Get the latest version the user has agreed to
  93.     disclaimerOKVersion=[[NSUserDefaults standardUserDefaults] objectForKey:@"DisclaimerOKVersion"];
  94.  
  95.     //If the user already agreed to this version, proceed. Otherwise display disclaimer.
  96.     if ([shortVersion isEqual:disclaimerOKVersion]) {
  97.         [self startup];
  98.     } else {
  99.         [disclaimerWindow setLevel:NSNormalWindowLevel];
  100.         [disclaimerWindow makeKeyAndOrderFront:self];
  101.     }
  102. }    
  103.  
  104. - (void) disclaimerOK:(id) sender {
  105.  
  106.     //Get our short version string
  107.     NSString* shortVersion;
  108.     
  109.     if ([[NSBundle mainBundle] respondsToSelector:@selector(objectForInfoDictionaryKey:)]) {
  110.         shortVersion=[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
  111.     } else {
  112.         shortVersion=@"<Unknown: Probably Pre-Jaguar System>";
  113.     }
  114.  
  115.     //Remove disclaimer window
  116.     [disclaimerWindow orderOut:self];
  117.     disclaimerWindow=NULL;
  118.  
  119.     //Remember the user said ok for this version
  120.     [[NSUserDefaults standardUserDefaults] setObject:shortVersion forKey:@"DisclaimerOKVersion"];
  121.     [[NSUserDefaults standardUserDefaults] synchronize];
  122.  
  123.     //Go on with the startup
  124.     [self startup];
  125. }
  126.  
  127. - (void) disclaimerQuit:(id) sender {
  128.     [[NSApplication sharedApplication] terminate:self];
  129. }
  130.  
  131. - (void) startup {
  132.     terminating=NO;
  133.     imageGrabbed=NO;
  134.     cameraGrabbing=NO;
  135.     cameraMediaCount=0;
  136.     [self setupToolbar];
  137.     [window setLevel:NSNormalWindowLevel];
  138.     [window makeKeyAndOrderFront:self];
  139.     image=[[NSImage alloc] init];
  140.     [image setCacheDepthMatchesImageDepth:YES];            //We have to set this to work with thousands of colors
  141.     imageRep=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL    //Set up just to avoid a NIL imageRep
  142.                                                      pixelsWide:320
  143.                                                      pixelsHigh:240
  144.                                                   bitsPerSample:8    
  145.                                                 samplesPerPixel:3
  146.                                                        hasAlpha:NO
  147.                                                        isPlanar:NO
  148.                                                  colorSpaceName:NSDeviceRGBColorSpace
  149.                                                     bytesPerRow:0
  150.                                                    bitsPerPixel:0];
  151.     assert (imageRep);
  152.     memset([imageRep bitmapData],0,[imageRep bytesPerRow]*[imageRep pixelsHigh]);
  153.     [image addRepresentation:imageRep]; 
  154.     [previewView setImage:image];
  155.     [window makeKeyAndOrderFront:self];
  156.     [window display];
  157.     [central startupWithNotificationsOnMainThread:YES recognizeLaterPlugins:YES];
  158. }
  159.  
  160. - (void) dealloc {
  161.     [central shutdown];
  162.     [previewView setImage:NULL];
  163.     [image release];
  164.     [super dealloc];
  165. }
  166.  
  167. - (IBAction)brightnessChanged:(id)sender {
  168.     [driver setBrightness:[brightnessSlider floatValue]];
  169. }
  170.  
  171. - (IBAction)contrastChanged:(id)sender {
  172.     [driver setContrast:[contrastSlider floatValue]];
  173. }
  174.  
  175. - (IBAction)gammaChanged:(id)sender {
  176.     [driver setGamma:[gammaSlider floatValue]];
  177. }
  178.  
  179. - (IBAction)sharpnessChanged:(id)sender {
  180.     [driver setSharpness:[sharpnessSlider floatValue]];
  181. }
  182.  
  183. - (IBAction)saturationChanged:(id)sender {
  184.     [driver setSaturation:[saturationSlider floatValue]];
  185. }
  186.  
  187. - (IBAction)manGainChanged:(id)sender {
  188.     float gain=[gainSlider floatValue];
  189.     float shutter=[shutterSlider floatValue];
  190.     BOOL man=[manGainCheckbox intValue];
  191.     [driver setGain:gain];
  192.     [driver setShutter:shutter];
  193.     [driver setAutoGain:!man];
  194.     [gainSlider setEnabled:man&[driver canSetGain]];
  195.     [shutterSlider setEnabled:man&[driver canSetShutter]];
  196. }
  197.  
  198. - (IBAction)gainChanged:(id)sender {
  199.     [self manGainChanged:self];
  200. }
  201.  
  202. - (IBAction)shutterChanged:(id)sender {
  203.     [self manGainChanged:self];
  204. }
  205.  
  206. - (IBAction)formatChanged:(id)sender {
  207.     NSRect winFrame;
  208.     NSRect screenFrame;
  209.     NSSize minWinSize;
  210.     //Part one: Set new driver format
  211.     CameraResolution res=(CameraResolution)([sizePopup indexOfSelectedItem]+1);
  212.     int fps=5*[fpsPopup indexOfSelectedItem]+5;
  213.     if (driver==NULL) return;
  214.     if ([driver supportsResolution:res fps:fps]) {
  215.         [driver setResolution:res fps:fps];
  216.         res=[driver resolution];
  217.         fps=[driver fps]/5-1;
  218.         [sizePopup selectItemAtIndex:((int)res)-1];
  219.         [fpsPopup selectItemAtIndex:fps];
  220.     }
  221. //Part two: Check if our imageRep still fits to the driver size
  222.     if (imageRep) {
  223.         if (([driver width]!=[imageRep pixelsWide])||([driver height]!=[imageRep pixelsHigh])) {
  224.             [previewView setImage:NULL];
  225.             if (image) [image release];
  226.             if (imageRep) [imageRep release];
  227.             image=[[NSImage alloc] init];
  228.             [image setCacheDepthMatchesImageDepth:YES];    //We want to work with thousands of colors (don't ask...)
  229.             imageRep=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
  230.                                                              pixelsWide:[driver width]
  231.                                                              pixelsHigh:[driver height]
  232.                                                           bitsPerSample:8
  233.                                                         samplesPerPixel:3
  234.                                                                hasAlpha:NO
  235.                                                                isPlanar:NO
  236.                                                          colorSpaceName:NSDeviceRGBColorSpace
  237.                                                             bytesPerRow:0
  238.                                                            bitsPerPixel:0];
  239.             assert(imageRep);
  240.             imageGrabbed=NO;
  241.             memset([imageRep bitmapData],0,[imageRep bytesPerRow]*[imageRep pixelsHigh]);
  242.             [image addRepresentation:imageRep];
  243.             [previewView setImage:image];
  244.             winFrame=[window frame];
  245.             screenFrame=[[window screen] frame];
  246.             minWinSize.width=[driver width]+40;
  247.             minWinSize.height=[driver height]+48;
  248. //If the window is to small to show the whole preview, resize it to fit
  249.             if ((minWinSize.width>winFrame.size.width)||(minWinSize.height>winFrame.size.height)) {
  250.                 if (minWinSize.width>winFrame.size.width) {
  251.                     winFrame.origin.x-=(minWinSize.width-winFrame.size.width)/2;
  252.                     winFrame.size.width=minWinSize.width;
  253.                 }
  254.                 if (minWinSize.height>winFrame.size.height) {
  255.                     winFrame.origin.y-=minWinSize.height-winFrame.size.height;
  256.                     winFrame.size.height=minWinSize.height;
  257.                 }
  258.                 winFrame.origin.x=MAX(winFrame.origin.x,screenFrame.origin.x); //Ensure that we don't resize left out of the screen
  259.                 [window setFrame:winFrame display:YES animate:YES];
  260.             }
  261.         }
  262.     }
  263. }
  264.  
  265. - (IBAction)compressionChanged:(id)sender {
  266.     short c=[compressionSlider floatValue]*(float)[driver maxCompression]+0.2f;
  267.     [driver setCompression:c];
  268. }
  269.  
  270. - (IBAction)whiteBalanceChanged:(id)sender {
  271.     WhiteBalanceMode wb=[whiteBalancePopup indexOfSelectedItem]+1;
  272.     [driver setWhiteBalanceMode:wb];
  273. }
  274.  
  275. - (IBAction)blackwhiteCheckboxChanged:(id)sender {
  276.     BOOL BlackWhite =[blackwhiteCheckbox intValue];
  277.     [driver setBlackWhiteMode:BlackWhite];
  278. }
  279.  
  280. - (IBAction)ledCheckboxChanged:(id)sender {
  281.     BOOL ledOn =[ledCheckbox intValue];
  282.     [driver setLed:ledOn];
  283. }
  284.  
  285. - (IBAction)horizontalFlipChanged:(id)sender {
  286.     BOOL flip=[horizontalFlipCheckbox intValue];
  287.     [driver setHFlip:flip];
  288. }
  289.  
  290. - (void) setImageOfToolbarItem:(NSString*)ident to:(NSString*)img {
  291.     NSToolbar* toolbar=[window toolbar];
  292.     if (toolbar) {
  293.         NSArray* items=[toolbar items];
  294.         if (items) {
  295.             int i;
  296.             for (i=0;i<[items count];i++) {
  297.                 NSToolbarItem* item=[items objectAtIndex:i];
  298.                 if (item) {
  299.                     if ([[item itemIdentifier] isEqualToString:ident]) {
  300.                         [item setImage:[NSImage imageNamed:img]];
  301.                     }
  302.                 }
  303.             }
  304.         }
  305.     }
  306. }
  307.  
  308. - (void) startMovieRecording {
  309.     NSString* parentPath;
  310.     NSString* path=NULL;
  311.     int i=1;
  312.     BOOL found=NO;
  313.     NSUserDefaults* settings=[NSUserDefaults standardUserDefaults];
  314.     NSString* movieCompressionType;
  315.     float movieCompressionQuality;
  316.     if (movieRecorder) return;
  317.     if (!driver) return;
  318.     parentPath=[settings objectForKey:MovieSavePathPrefsKey];
  319.     parentPath=[parentPath stringByExpandingTildeInPath];
  320.     while (!found) {
  321.         path=[NSString stringWithFormat:@"macam movie %i.mov",i];
  322.         path=[parentPath stringByAppendingPathComponent:path];
  323.         if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL]) i++;
  324.         else found=YES;
  325.         if (i>9999) return;    //avoid infinite loops - more than 10000 movies in one directory is unlikely... 
  326.     }
  327.     [self setImageOfToolbarItem:RecordMovieToolbarItemIdentifier to:@"RecordMovieActiveToolbarItem"];
  328.     movieRecordStart=0.0;    //0.0 = Mark as unset, first imageReady will set this
  329.     movieLastCapturedImage=0.0;    //no image captured yet
  330.     movieMinCaptureInterval=[settings floatForKey:MovieSampleDurationPrefsKey];
  331.     movieRecordingTimeFactor=[settings floatForKey:MoviePlaybackFactorPrefsKey];
  332.     if (movieRecordingTimeFactor<=0.0) movieRecordingTimeFactor=1.0;
  333.     movieCompressionType=[settings objectForKey:MovieCompressionPrefsKey];
  334.     if (!movieCompressionType) movieCompressionType=@"JPEG";
  335.     movieCompressionQuality=[settings floatForKey:MovieQualityPrefsKey];
  336.     if ((movieCompressionQuality<0.0)||(movieCompressionQuality>1.0)) movieCompressionQuality=0.5;
  337.     movieRecorder=[[MyMovieRecorder alloc] initWithSize:NSMakeSize([driver width],[driver height])
  338.                                             compression:movieCompressionType
  339.                                                 quality:movieCompressionQuality
  340.                                                    path:path];
  341. }
  342.  
  343. - (void) stopMovieRecording {
  344.     if (movieRecorder) {
  345.         NSString* tempMoviePath;
  346.         double time;
  347.         if (movieRecordStart>0.0) time=(CFAbsoluteTimeGetCurrent()-movieRecordStart)/movieRecordingTimeFactor;
  348.         else time=1.0f;
  349.         [self setImageOfToolbarItem:RecordMovieToolbarItemIdentifier to:@"RecordMovieToolbarItem"];   
  350.         [movieRecorder finishRecordingAt:time];
  351.         tempMoviePath=[movieRecorder moviePath];
  352.         [movieRecorder keepMovieFile];
  353.         [movieRecorder release];
  354.         movieRecorder=NULL;
  355.         [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:tempMoviePath
  356.                                                                                 display:YES];
  357.     }
  358.  
  359. }
  360.  
  361. - (IBAction)doRecordMovie:(id)sender {
  362.     if (movieRecorder) [self stopMovieRecording];
  363.     else [self startMovieRecording];
  364. }
  365.  
  366. - (IBAction)doGrab:(id)sender {
  367.     if (cameraGrabbing) {
  368.         cameraGrabbing=[driver stopGrabbing];
  369.         if (!cameraGrabbing) {
  370.             [statusText setStringValue:LStr(@"Status: Pausing")];
  371.         }
  372.     } else {
  373.         cameraGrabbing=[driver startGrabbing];
  374.         if (cameraGrabbing) {
  375.             [self setImageOfToolbarItem:PlayToolbarItemIdentifier to:@"PauseToolbarItem"];
  376.             [statusText setStringValue:LStr(@"Status: Playing")];
  377.             [fpsPopup setEnabled:NO];
  378.             [sizePopup setEnabled:NO];
  379.             [compressionSlider setEnabled:NO];
  380.             [driver setImageBuffer:[imageRep bitmapData] bpp:3 rowBytes:[driver width]*3];
  381.         }
  382.     }
  383. }
  384.  
  385.  
  386. //
  387. // This only works if the file already exists...
  388. //
  389. // usage:
  390. //
  391. //  FSSpec spec;
  392. //  if (![self makeFSSpec:&spec fromPath:path]) 
  393. //    NSLog(@"fsspec: vol=%d parent=%08x name=%s", spec.vRefNum, spec.parID, &spec.name[1]);
  394. //
  395. - (BOOL) makeFSSpec:(FSSpec *) specPtr fromPath:(NSString *) inPath
  396. {
  397.     FSRef fsRef;
  398.     
  399.     OSStatus status = FSPathMakeRef((unsigned char *) [inPath fileSystemRepresentation], &fsRef, NULL);
  400.     
  401.     if (status == noErr)
  402.         status = FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL, NULL, specPtr, NULL);
  403.     
  404.     return status != noErr; // return YES if there was a problem
  405. }
  406.  
  407.  
  408. // This might work if file doesn't exist yet, but will need to be fixed
  409. /*
  410. OSStatus PathToFSSpec (NSString *path, FSSpec *outSpec)
  411. {
  412.     OSStatus err = noErr;
  413.     FSRef ref;
  414.     Boolean isDirectory;
  415.     FSCatalogInfo info;
  416.     CFStringRef pathString = NULL;
  417.     CFURLRef pathURL = NULL;
  418.     CFURLRef parentURL = NULL;
  419.     CFStringRef nameString = NULL;
  420.     
  421.     const char *inPath = [path cString];
  422.     
  423.     // First, try to create an FSRef for the full path 
  424.     if (err == noErr) {
  425.         err = FSPathMakeRef ((UInt8 *) inPath, &ref, &isDirectory);
  426.     }
  427.     
  428.     if (err == noErr) {
  429.         // It's a directory or a file that exists; convert directly into an FSSpec:
  430.         err = FSGetCatalogInfo (&ref, kFSCatInfoNone, NULL, NULL, outSpec, NULL);
  431.     } else {
  432.         // The harder case.  The file doesn't exist.
  433.         err = noErr;
  434.         
  435.         // Get a CFString for the path
  436.         if (err == noErr) {
  437.             pathString = CFStringCreateWithCString (CFAllocatorGetDefault (), inPath, CFStringGetSystemEncoding ());
  438.             if (pathString == NULL) { err = memFullErr; }
  439.         }
  440.         
  441.         // Get a CFURL for the path
  442.         if (err == noErr) {
  443.             pathURL = CFURLCreateWithFileSystemPath (CFAllocatorGetDefault (), 
  444.                                                      pathString, kCFURLPOSIXPathStyle, 
  445.                                                      false ); // Not a directory 
  446.             if (pathURL == NULL) { err = memFullErr; }
  447.         }
  448.         
  449.         // Get a CFURL for the parent
  450.         if (err == noErr) {
  451.             parentURL = CFURLCreateCopyDeletingLastPathComponent (CFAllocatorGetDefault (), pathURL);
  452.             if (parentURL == NULL) { err = memFullErr; }
  453.         }
  454.         
  455.         // Build an FSRef for the parent directory, which must be valid to make an FSSpec
  456.         if (err == noErr) {
  457.             Boolean converted = CFURLGetFSRef (parentURL, &ref);
  458.             if (!converted) { err = fnfErr; } 
  459.         }
  460.         
  461.         // Get the node ID of the parent directory
  462.         if (err == noErr) {
  463.             err = FSGetCatalogInfo(&ref, kFSCatInfoNodeFlags|kFSCatInfoNodeID, &info, NULL, outSpec, NULL);
  464.         }
  465.         
  466.         // Get a CFString for the file name
  467.         if (err == noErr) {
  468.             nameString = CFURLCopyLastPathComponent (pathURL);
  469.             if (nameString == NULL) { err = memFullErr; }
  470.         }
  471.         
  472.         // Copy the string into the FSSpec
  473.         if (err == noErr) {     
  474.             Boolean converted = CFStringGetPascalString (nameString, outSpec->name, sizeof (outSpec->name), 
  475.                                                          CFStringGetSystemEncoding ());
  476.             if (!converted) { err = fnfErr; }
  477.             
  478.         }
  479.         
  480.         // Set the node ID in the FSSpec
  481.         if (err == noErr) {
  482.             outSpec->parID = info.nodeID;
  483.         }
  484.     }
  485.     
  486.     // Free allocated memory
  487.     if (pathURL != NULL)    { CFRelease (pathURL);    }
  488.     if (pathString != NULL) { CFRelease (pathString); }
  489.     if (parentURL != NULL)  { CFRelease (parentURL);  }
  490.     if (nameString != NULL) { CFRelease (nameString); }
  491.     
  492.     return err;
  493. }
  494. */
  495.  
  496.  
  497. #define ToFix(A) ((Fixed)(((long)(A))<<16))
  498.  
  499.  
  500. - (BOOL) addFrameToVideoTrack:(Media) videoTrackMedia withImage:(NSBitmapImageRep *) theImage
  501. {
  502.     BOOL problem = NO;
  503.     double duration = 0.1; // try 1/10 of a second 
  504.     OSErr err;
  505.     
  506.     // Variables needed for conversion
  507.     
  508.     CodecQ codecSpatialQuality = codecHighQuality; // codecNormalQuality
  509.     CodecType codecType = kJPEGCodecType;
  510.     
  511.     Handle imageData;
  512.     ImageDescriptionHandle imageDescription;
  513.     
  514.     Rect srcRect;
  515.     GWorldPtr gw;
  516.     PixMapHandle pm = NULL;
  517.     long maxDataLength;
  518.     
  519.     // Setup GWorld / PixMap
  520.     
  521.     SetRect(&srcRect, 0, 0, [theImage pixelsWide], [theImage pixelsHigh]);
  522.     err = QTNewGWorldFromPtr(&gw, ([theImage bitsPerPixel] == 24) ? k24RGBPixelFormat : k32ARGBPixelFormat,
  523.                              &srcRect, NULL, NULL, 0, [theImage bitmapData], [theImage bytesPerRow]);
  524.     if (err) 
  525.     {
  526.         problem = YES;
  527.         NSLog(@"QTNewGWorldFromPtr returned %i", err);
  528.     }
  529.     
  530.     if (!problem) 
  531.         pm = GetGWorldPixMap(gw);
  532.     
  533.     // Determine compressed data size
  534.     
  535.     if (!problem) 
  536.         err = GetMaxCompressionSize(pm, &srcRect, 24, codecSpatialQuality, codecType, NULL, &maxDataLength);
  537.     
  538.     if (err) 
  539.     {
  540.         problem = YES;
  541.         NSLog(@"GetMaxCompressionSize returned %i", err);
  542.     }
  543.     
  544.     // Allocate appropiate buffers
  545.     
  546.     imageData = NewHandle(maxDataLength);
  547.     
  548.     NSAssert(imageData, @"addFrame: at: Could not allocate buffer for compressed image data");
  549.     if (imageData == NULL) 
  550.         problem = YES;
  551.     
  552.     imageDescription = (ImageDescriptionHandle) NewHandle(sizeof(ImageDescription));
  553.     
  554.     NSAssert(imageDescription, @"addFrame: at: Could not allocate buffer for compressed image description");
  555.     if (imageDescription == NULL) 
  556.         problem = YES;
  557.     
  558.     // Do image compression
  559.     
  560.     HLock(imageData);
  561.     
  562.     if (!problem) 
  563.         err = CompressImage(pm, &srcRect, codecSpatialQuality, codecType, imageDescription, *imageData);
  564.     
  565.     if (err) 
  566.     {
  567.         problem = YES;
  568.         NSLog(@"CompressImage returned %i", err);
  569.     }
  570.     
  571.     HUnlock(imageData);
  572.  
  573.     // Insert image
  574.     
  575.     if (!problem) 
  576.         err = AddMediaSample(videoTrackMedia, imageData, 0, (**imageDescription).dataSize,
  577.                              duration * 600.0f, (SampleDescriptionHandle) imageDescription, 1, 0, NULL);
  578.     
  579.     if (err) 
  580.     {
  581.         problem = YES;
  582.         NSLog(@"AddMediaSample returned %i", err);
  583.     }
  584.     
  585.     // Cleanup
  586.     
  587.     DisposeGWorld(gw);
  588.     DisposeHandle(imageData);
  589.     DisposeHandle((Handle) imageDescription);
  590.     
  591.     return problem;
  592. }
  593.  
  594.  
  595. // outstanding issues with saving clips:
  596. //
  597. // non-bitmaps do not work yet
  598. // preferred movie compression is ignored
  599. //
  600. - (BOOL) addImageToMedia:(Media) videoTrackMedia imageData:(NSData *) data of:(NSString *) type
  601. {
  602.     BOOL problem = NO;
  603.     
  604.     NSBitmapImageRep * bitmap = (NSBitmapImageRep *) data;
  605.     
  606.     problem = [self addFrameToVideoTrack:videoTrackMedia withImage:bitmap];
  607.  
  608. //    if (type is @"bitmap") 
  609.      
  610. /*
  611.     //Find wanted compression
  612.     if ([cType isEqualToString:@"RAW"]) {
  613.         codecType=kRawCodecType;
  614.         codecSpatialQuality=codecLosslessQuality;
  615.     } else if ([cType isEqualToString:@"JPEG"]) {
  616.         codecType=kJPEGCodecType;
  617.         codecSpatialQuality=((float)codecLosslessQuality)*cQual;
  618.     } else {
  619.         //FIXME: [self dealloc] here?
  620.         return NULL;
  621.     }
  622. */    
  623.         
  624.     return problem;
  625. }
  626.  
  627.  
  628. - (BOOL) addVideoTrack:(Movie) theMovie withContent:(NSArray *) media of:(NSString *) type
  629. {
  630.     BOOL problem = NO;
  631.     OSErr err = noErr;
  632.     Track videoTrack;
  633.     Media videoTrackMedia = NULL;
  634.     int index;
  635.     
  636.     if ([media count] < 1) 
  637.         return YES; // This is a problem
  638.     
  639.     // Create the video track
  640.     
  641.     NSBitmapImageRep * firstImage = [media objectAtIndex:0];
  642.     
  643.     if (!problem) 
  644.         videoTrack = NewMovieTrack(theMovie, ToFix([firstImage pixelsWide]), ToFix([firstImage pixelsHigh]), kNoVolume);
  645.     
  646.     err = GetMoviesError();
  647.     if (err != noErr) 
  648.     {
  649.         problem = YES;
  650.         NSLog(@"NewMovieTrack returned %i", err);
  651.     }
  652.     
  653.     // Create the track media
  654.     
  655.     if (!problem) 
  656.         videoTrackMedia = NewTrackMedia(videoTrack, VideoMediaType, 600, NULL, 0);
  657.     
  658.     err = GetMoviesError();
  659.     if (err != noErr) 
  660.     {
  661.         problem = YES;
  662.         NSLog(@"NewTrackMedia returned %i", err);
  663.     }
  664.     
  665.     // Start recording session
  666.     
  667.     if (!problem) 
  668.         err = BeginMediaEdits(videoTrackMedia);
  669.     
  670.     if (err) 
  671.     {
  672.         problem = YES;
  673.         NSLog(@"BeginMediaEdits returned %i", err);
  674.     }
  675.     
  676.     // Loop through array in media, add each image
  677.     
  678.     if (!problem) 
  679.         for (index = 0; index < [media count]; index++)
  680.             problem = [self addImageToMedia:videoTrackMedia imageData:[media objectAtIndex:index] of:type];
  681.     
  682.     // Now insert the media into the track
  683.     
  684.     if (!problem) 
  685.         err = InsertMediaIntoTrack(videoTrack, 0, 0, GetMediaDuration(videoTrackMedia), ToFix(1));
  686.     
  687.     if (err) 
  688.     {
  689.         problem = YES;
  690.         NSLog(@"InsertMediaIntoTrack returned %i", err);
  691.     }
  692.     
  693.     if (!problem) 
  694.         err = EndMediaEdits(videoTrackMedia);
  695.     
  696.     if (err) 
  697.     {
  698.         problem = YES;
  699.         NSLog(@"EndMediaEdits returned %i", err);
  700.     }
  701.     
  702.     return problem;
  703. }
  704.  
  705.  
  706. - (BOOL) saveClip:(NSArray *) media ofType:(NSString *) type toFile:(NSString *) filename with:(NSDictionary *) attributes
  707. {
  708.     BOOL problem = NO;
  709.     Movie theMovie = NULL;
  710.     FSSpec spec;
  711.     OSErr err = 0;
  712.     short resRefNum = 0;
  713.     short resId = movieInDataForkResID;
  714.     
  715.     // Create the file in case it doesn't exist yet
  716.     
  717.     problem = ![[NSFileManager defaultManager] createFileAtPath:filename contents:[NSMutableData dataWithLength:37] attributes:attributes];
  718.  
  719.     // Make an FSSpec for the file now that it exists
  720.  
  721.     if (!problem) 
  722.         problem = [self makeFSSpec:&spec fromPath:filename];
  723.     
  724.     // Create the movie file
  725.  
  726.     if (!problem) 
  727.         err = CreateMovieFile(&spec, 'TVOD', smCurrentScript,
  728.                               createMovieFileDeleteCurFile | createMovieFileDontCreateResFile,
  729.                               &resRefNum, &theMovie);
  730.     
  731.     if (err) 
  732.     {
  733.         problem = YES;
  734.         NSLog(@"CreateMovieFile failed with error %i", err);
  735.     }
  736.     
  737.     if (!problem) 
  738.         problem = [self addVideoTrack:theMovie withContent:media of:type];
  739.     
  740.     err = AddMovieResource(theMovie, resRefNum, &resId, NULL);
  741.     
  742.     if (err) 
  743.     {
  744.         problem = YES;
  745.         NSLog(@"AddMovieResource failed with error %i", err);
  746.     }
  747.     
  748.     if (resRefNum != 0)
  749.         err = CloseMovieFile(resRefNum);
  750.     
  751.     if (theMovie) 
  752.         DisposeMovie(theMovie);
  753.     
  754.     return problem; // hopefully NO
  755. }
  756.  
  757.  
  758. /*
  759.  *  Save the image in a file
  760.  *  Should reale be called saveMedia, since it can be a clip too
  761.  *
  762.  *  Use the preferred image format, stored in preferences, unless camera uses jpeg
  763.  *
  764.  *  Convert image formats if necessary
  765.  *
  766.  *  Parameters:
  767.  *  - image data (NSDictionary *) - must contain type & data keys, with either "bitmap" and (NSBitmapImageRep *) or "jpeg" and jpeg data
  768.  *  - base filename (NSString *)
  769.  *  - attributes (NSDictionary*) - passed from the save dialog
  770.  *
  771.  * Returns YES if everything OK; NO if there was a problem
  772.  */
  773. - (BOOL) saveImage:(NSDictionary *) media toFile:(NSString *) baseFilename with:(NSDictionary *) attributes
  774. {
  775.     BOOL problem = NO;
  776.     NSData * mediaData = NULL;
  777.     NSString * fileExtension;
  778.     NSString * clipType = NULL;
  779.     
  780.     // If the camera stores images in jpeg format, then this wil be used for the file;
  781.     // otherwise the user preferred format willl be used
  782.     
  783.     if ([[media objectForKey:@"type"] isEqualToString:@"jpeg"]) 
  784.     {
  785.         mediaData = [media objectForKey:@"data"];
  786.         fileExtension = @"jpg";
  787.     } 
  788.     else if ([[media objectForKey:@"type"] isEqualToString:@"bitmap"]) 
  789.     {
  790.         int imageFileType;
  791.         NSBitmapImageRep * theImage = [media objectForKey:@"data"];
  792.         NSString * preferredImageType = [[NSUserDefaults standardUserDefaults] objectForKey:SnapshotFormatPrefsKey];
  793.         
  794.         // Find the user-preferred format for image files
  795.         // Map the preference menu choice to the appropriate enum
  796.         
  797.         if ([preferredImageType isEqualToString:@"JPEG"]) 
  798.         {
  799.             imageFileType = NSJPEGFileType;
  800.             fileExtension = @"jpg";
  801.         } 
  802.         else if ([preferredImageType isEqualToString:@"PNG"]) 
  803.         {
  804.             imageFileType = NSPNGFileType;
  805.             fileExtension = @"png";
  806.         }
  807.         else if ([preferredImageType isEqualToString:@"BMP"]) 
  808.         {
  809.             imageFileType = NSBMPFileType;
  810.             fileExtension = @"bmp";
  811.         }
  812.         else if ([preferredImageType isEqualToString:@"GIF"]) 
  813.         {
  814.             imageFileType = NSGIFFileType;
  815.             fileExtension = @"gif";
  816.         }
  817.         else // if ([preferredImageType isEqualToString:@"TIFF"]) // default is TIFF
  818.         {
  819.             imageFileType = NSTIFFFileType;
  820.             fileExtension = @"tiff";
  821.             mediaData = [theImage TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:0.0];
  822.         }
  823.         
  824.         if (!mediaData) 
  825.             mediaData = [theImage representationUsingType:imageFileType properties:nil];
  826.     }
  827.     else if ([[media objectForKey:@"type"] isEqualToString:@"clip"]) 
  828.     {
  829.         mediaData = [media objectForKey:@"data"];
  830.         clipType = [media objectForKey:@"clip-type"];
  831.         fileExtension = @"mov";
  832.     }
  833.     else
  834.     {
  835.         // error, either no "type" or unknown type
  836.         problem = YES;
  837.     }
  838.     
  839.     if (mediaData) 
  840.     {
  841.         long saveIndex = 1;
  842.         BOOL indexFound = NO;
  843.         NSString * filename;
  844.         
  845.         while ((!indexFound) && (!problem)) // Find a free index
  846.         {
  847.             // build path without extension
  848.             filename = [baseFilename stringByAppendingString:[NSString stringWithFormat:@" %04i", saveIndex]];
  849.             
  850.             if ([filename completePathIntoString:NULL caseSensitive:NO matchesIntoArray:NULL filterTypes:NULL] == 0) 
  851. //          if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) // old check
  852.                 indexFound = YES;
  853.             else 
  854.                 saveIndex++;
  855.             
  856.             if (saveIndex > 9999) 
  857.                 problem = YES; // That's too high!
  858.         }
  859.         
  860.         // build path with extension
  861.         filename = [baseFilename stringByAppendingString:[NSString stringWithFormat:@" %04i.%@", saveIndex, fileExtension]];
  862.         
  863.         if (!problem) 
  864.         {
  865.             if (clipType == NULL) 
  866.             {
  867.                 problem = ![[NSFileManager defaultManager] createFileAtPath:filename contents:mediaData attributes:attributes];
  868.             }
  869.             else 
  870.             {
  871.                problem = [self saveClip:(NSArray *) mediaData ofType:clipType toFile:filename with:attributes];
  872.             }
  873.         }
  874.     } 
  875.     else 
  876.         problem = YES;
  877.     
  878.     return !problem;
  879. }
  880.  
  881. - (void) doSaveImage:(id)sender {
  882.     NSArray* controllers;
  883.     int i;
  884.     NSString* imageType;
  885.     MyImageDocument* doc;
  886.     NSData* imageData;
  887.  
  888.     imageType=[[NSUserDefaults standardUserDefaults] objectForKey:SnapshotFormatPrefsKey];
  889.     if ([imageType isEqualToString:@"JPEG"]) {
  890.         imageType=@"JPEG Image";
  891.     } else if ([imageType isEqualToString:@"PNG"]) {
  892.         imageType=@"PNG Image";
  893.     } else if ([imageType isEqualToString:@"GIF"]) {
  894.         imageType=@"GIF Image";
  895.     } else if ([imageType isEqualToString:@"BMP"]) {
  896.         imageType=@"BMP Image";
  897.     } else {
  898.         imageType=@"TIFF Image";
  899.     }
  900.         
  901.     doc=[[NSDocumentController sharedDocumentController] openUntitledDocumentOfType:imageType 
  902.                                                                             display:NO];
  903.     imageData=[imageRep TIFFRepresentation];
  904.     [doc loadDataRepresentation:imageData ofType:@"TIFF Image"];
  905.     
  906.     [doc setQuality:[[NSUserDefaults standardUserDefaults] floatForKey:SnapshotQualityPrefsKey]];
  907.     //Show image window behind control window
  908.     controllers=[doc windowControllers];
  909.     if (controllers) {
  910.         for (i=0;i<[controllers count];i++) {
  911.             [[[controllers objectAtIndex:i] window] orderWindow:NSWindowBelow 
  912.                                                      relativeTo:[window windowNumber]];
  913.         }
  914.     }
  915.     [doc setFileType:imageType];
  916.     [doc updateChangeCount:NSChangeDone];
  917. }
  918.  
  919. - (void) doSavePrefs:(id)sender {
  920.     if ((driver)&&(central)) {
  921.         [central saveCameraSettingsAsDefaults:driver];
  922.     }
  923. }
  924.  
  925. - (void) toggleSettingsDrawer:(id) sender {
  926.     NSDrawerState state=[settingsDrawer state];
  927.     if ((state==NSDrawerOpeningState)||(state==NSDrawerOpenState)) {
  928.         [settingsDrawer close];
  929.         [inspectorDrawer close];
  930.     } else {
  931.         [settingsDrawer openOnEdge:NSMaxXEdge];
  932.         if (inspector) {
  933.             [inspectorDrawer openOnEdge:NSMinXEdge];
  934.         }
  935.     }
  936. }
  937.  
  938. - (IBAction)doQuit:(id)sender {
  939.     MyCameraDriver* oldDriver=driver;
  940.     driver=NULL;
  941.     terminating=YES;
  942.     [central shutdown];
  943.     if (oldDriver) [oldDriver shutdown]; //if there's a driver, we shut down when the driver has shut down
  944.     else [[NSApplication sharedApplication] terminate:self];
  945. }
  946.  
  947.  
  948. - (IBAction)doNextCam:(id)sender {
  949.     short idx=-1;
  950.     unsigned long cid;
  951.     MyCameraDriver* oldDriver;
  952.     if (driver) idx=[central indexOfCamera:driver];    //Get our current index
  953.     else idx=-1;
  954.     idx+=1;                        //Find out the next index to use
  955.     idx%=[central numCameras];
  956.     cid=[central idOfCameraWithIndex:idx];        //Get the camera id matching to the new index
  957.     if (cid<1) {
  958.         [statusText setStringValue:LStr(@"Status: Camera switch failed")];
  959.         return;
  960.     }
  961.     oldDriver=driver;                    //Make a copy to avoid interference of shutdown and startup
  962.     driver=NULL;
  963.     if (oldDriver) [oldDriver shutdown];        //Remove the old cam
  964.     [self cameraDetected:cid];                //Act as we have a freshly plugged cam
  965. }
  966.  
  967. - (void)askDownloadMediaSheetEnded:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void  *)con {
  968. //Stop sheet so it is away for displaying the new panel
  969.     [sheet orderOut:self];
  970.     [[NSApplication sharedApplication] endSheet:sheet];
  971.     if (returnCode==NSOKButton) [self doDownloadMedia:self];
  972. }
  973.  
  974. - (IBAction)doDeleteAll:(id)sender {
  975.     // Should put up a sheet to double-check!
  976.     [driver deleteAll];
  977.     [self updateCameraMediaCount];
  978. }
  979.  
  980. - (IBAction)doDeleteOne:(id)sender {
  981.     long idx = cameraMediaCount-1;
  982.     // Should put up a sheet, get the number to delete?
  983.     [driver deleteOne:idx];
  984.     [self updateCameraMediaCount];
  985. }
  986.  
  987. - (IBAction)doDeleteLast:(id)sender {
  988.     // Should put up a sheet to double-check!
  989.     [driver deleteLast];
  990.     [self updateCameraMediaCount];
  991. }
  992.  
  993. - (IBAction)doTakeStillImage:(id)sender {
  994.     CameraError err = [driver captureOne];
  995.     [self updateCameraMediaCount];
  996.     if (!err) {
  997.         // download the new image
  998.         // create a window with the picture
  999.     }
  1000. }
  1001.  
  1002. - (IBAction)doDownloadMedia:(id)sender {
  1003.     NSSavePanel* panel;
  1004.     [self updateCameraMediaCount];
  1005.     if (cameraMediaCount<1) return;
  1006.     panel=[NSSavePanel savePanel];
  1007.     [panel setPrompt:LStr(@"Save like this")];
  1008.     [panel setCanSelectHiddenExtension:YES];
  1009. //  [panel setRequiredFileType:@"tiff"];
  1010.     [panel setRequiredFileType:@"[extension]"];
  1011.     [panel beginSheetForDirectory:[@"~/Pictures" stringByExpandingTildeInPath]
  1012.                              file:NULL
  1013.                    modalForWindow:window
  1014.                     modalDelegate:self
  1015.                    didEndSelector:@selector(downloadSaveSheetEnded:returnCode:contextInfo:)
  1016.                       contextInfo:NULL];
  1017. }
  1018.  
  1019. - (void)downloadSaveSheetEnded:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void*)con {
  1020.     NSOpenPanel* panel=(NSOpenPanel*)sheet;
  1021.     long i;
  1022.     NSDictionary* media;
  1023.     NSString* baseName=[[panel filename] stringByDeletingPathExtension];
  1024.     BOOL problem=NO;
  1025.     NSDictionary* attributes;
  1026.     NSProgressIndicator* bar;
  1027.     
  1028.     //Stop sheet so the status is visible
  1029.     [sheet orderOut:self];
  1030.     [[NSApplication sharedApplication] endSheet:sheet];
  1031.     if (returnCode!=NSFileHandlingPanelOKButton) {
  1032.         [statusText setStringValue:LStr(@"Status: Media not downloaded")];
  1033.         return;
  1034.     }
  1035.     attributes=[NSDictionary dictionaryWithObject:    [NSNumber numberWithBool:[panel isExtensionHidden]]
  1036.                                            forKey:    NSFileExtensionHidden];
  1037.     [self updateCameraMediaCount];
  1038.     if (cameraMediaCount < 1) 
  1039.         return;
  1040.  
  1041.     bar=[[[NSProgressIndicator alloc] initWithFrame:[statusText frame]] autorelease];
  1042.     [bar setIndeterminate:NO];
  1043.     [bar setMinValue:0.0];
  1044.     [bar setMaxValue:((double)(cameraMediaCount))];
  1045.     [bar setDoubleValue:0.0];
  1046.     [[statusText superview] addSubview:bar];
  1047.     [bar display];
  1048.     
  1049.     // Download each image and save to disk
  1050.     
  1051.     for (i = 0; (i < cameraMediaCount) && (!problem); i++) // Iterate over all media objects
  1052.     {        
  1053.         media=[driver getStoredMediaObject:i];    // Get media
  1054.         if (media) 
  1055.         {
  1056.             [statusText setStringValue:[NSString stringWithFormat:LStr(@"Status: Downloading number %i"),i+1]];
  1057.             [statusText display];
  1058.             [bar setDoubleValue:((double)(i+1))];
  1059.             [bar displayIfNeeded];
  1060.             
  1061.             if (![self saveImage:media toFile:baseName with:attributes]) 
  1062.                 problem = YES;
  1063.             
  1064.             [[media retain] release];
  1065.         } 
  1066.         else 
  1067.             problem=YES;
  1068.     }
  1069.     
  1070.     [bar removeFromSuperview];
  1071.     
  1072.     if (problem) 
  1073.         [statusText setStringValue:LStr(@"Status: A problem occurred - please check files")];
  1074.     else 
  1075.         [statusText setStringValue:LStr(@"Status: Media downloaded from camera")];
  1076. }
  1077.  
  1078. - (void)cameraDetected:(unsigned long)cid {
  1079.     CameraError err;
  1080.     if (!driver) {
  1081.         err=[central useCameraWithID:cid to:&driver acceptDummy:NO];
  1082.         if (err) driver=NULL;
  1083.         if (driver!=NULL) {
  1084.             if ([driver hasSpecificName]) 
  1085.                 [statusText setStringValue:[LStr(@"Status: Connected to ") stringByAppendingString:[driver getSpecificName]]];
  1086.             else
  1087.                 [statusText setStringValue:[LStr(@"Status: Connected to ") stringByAppendingString:[central nameForID:cid]]];
  1088.             [driver retain];            //We keep our own reference
  1089.             [contrastSlider setEnabled:[driver canSetContrast]];
  1090.             [brightnessSlider setEnabled:[driver canSetBrightness]];
  1091.             [gammaSlider setEnabled:[driver canSetGamma]];
  1092.             [sharpnessSlider setEnabled:[driver canSetSharpness]];
  1093.             [saturationSlider setEnabled:[driver canSetSaturation]];
  1094.             [manGainCheckbox setEnabled:[driver canSetAutoGain]];
  1095.             [sizePopup setEnabled:YES];
  1096.             [fpsPopup setEnabled:YES];
  1097.             [whiteBalancePopup setEnabled:[driver canSetWhiteBalanceMode]];
  1098.             [horizontalFlipCheckbox setEnabled:[driver canSetHFlip]];
  1099.             [blackwhiteCheckbox setEnabled:[driver canBlackWhiteMode]];
  1100.             [ledCheckbox setEnabled:[driver canSetLed]];
  1101.  
  1102.             [whiteBalancePopup selectItemAtIndex:[driver whiteBalanceMode]-1];
  1103.             [gainSlider setEnabled:([driver canSetGain])&&(![driver isAutoGain])];
  1104.             [shutterSlider setEnabled:([driver canSetShutter])&&(![driver isAutoGain])];
  1105.             if ([driver maxCompression]>0) {
  1106.                 [compressionSlider setNumberOfTickMarks:[driver maxCompression]+1];
  1107.                 [compressionSlider setEnabled:YES];
  1108.             } else {
  1109.                 [compressionSlider setNumberOfTickMarks:2];
  1110.                 [compressionSlider setEnabled:NO];
  1111.             }
  1112.             [brightnessSlider setFloatValue:[driver brightness]];
  1113.             [contrastSlider setFloatValue:[driver contrast]];
  1114.             [saturationSlider setFloatValue:[driver saturation]];
  1115.             [gammaSlider setFloatValue:[driver gamma]];
  1116.             [sharpnessSlider setFloatValue:[driver sharpness]];
  1117.             [gainSlider setFloatValue:[driver gain]];
  1118.             [shutterSlider setFloatValue:[driver shutter]];
  1119.             [manGainCheckbox setIntValue:([driver isAutoGain]==NO)?1:0];
  1120.             [sizePopup selectItemAtIndex:[driver resolution]-1];
  1121.             [fpsPopup selectItemAtIndex:([driver fps]/5)-1];
  1122.             [compressionSlider setFloatValue:((float)[driver compression])
  1123.                 /((float)(([driver maxCompression]>0)?[driver maxCompression]:1))];
  1124.             [horizontalFlipCheckbox setIntValue:([driver hFlip]==YES)?1:0];
  1125.             [self formatChanged:self];
  1126.             cameraGrabbing=NO;
  1127.             if ([driver supportsCameraFeature:CameraFeatureInspectorClassName]) {
  1128.                 NSString* inspectorName=[driver valueOfCameraFeature:CameraFeatureInspectorClassName];
  1129.                 if (inspectorName) {
  1130.                     if (![@"MyCameraInspector" isEqualToString:inspectorName]) {
  1131.                         Class c=NSClassFromString(inspectorName);
  1132.                         inspector=[(MyCameraInspector*)[c alloc] initWithCamera:driver];
  1133.                         if (inspector) {
  1134.                             NSDrawerState state;
  1135.                             [inspectorDrawer setContentView:[inspector contentView]];
  1136.                             state=[settingsDrawer state];
  1137.                             if ((state==NSDrawerOpeningState)||(state==NSDrawerOpenState)) {
  1138.                                 [inspectorDrawer openOnEdge:NSMinXEdge];
  1139.                             }
  1140.                         }
  1141.                     }
  1142.                 }
  1143.             }
  1144.         } else {
  1145.             switch (err) {
  1146.                 case CameraErrorBusy:[statusText setStringValue:LStr(@"Status: Camera used by another app")]; break;
  1147.                 case CameraErrorNoPower:[statusText setStringValue:LStr(@"Status: Not enough USB bus power")]; break;
  1148.                 case CameraErrorNoCam:[statusText setStringValue:LStr(@"Status: Camera not found (this shouldn't happen)")]; break;
  1149.                 case CameraErrorNoMem:[statusText setStringValue:LStr(@"Status: Out of memory")]; break;
  1150.                 case CameraErrorUSBProblem:[statusText setStringValue:LStr(@"Status: USB communication problem")]; break;
  1151.                 case CameraErrorInternal:[statusText setStringValue:LStr(@"Status: Internal error (this shouldn't happen)")]; break;
  1152.                 case CameraErrorUnimplemented:[statusText setStringValue:LStr(@"Status: Unsupported")]; break;
  1153.                 default:[statusText setStringValue:LStr(@"Status: Unknown error (this shouldn't happen)")]; break;
  1154.             }
  1155.         }
  1156.     }
  1157. //Try to download images from the camera
  1158.     [self updateCameraMediaCount];
  1159.     if (cameraMediaCount>0) {
  1160.         if (cameraMediaCount==1) {
  1161.             NSBeginInformationalAlertSheet(LStr(@"Download media?"),
  1162.                                            LStr(@"Yes"),
  1163.                                            LStr(@"No"),
  1164.                                            NULL,
  1165.                                            window,
  1166.                                            self,
  1167.                                            @selector(askDownloadMediaSheetEnded:returnCode:contextInfo:),
  1168.                                            NULL,
  1169.                                            NULL,
  1170. LStr(@"The camera you just plugged in contains one stored image. Do you want to download them to your computer?"),cameraMediaCount);
  1171.         } else {
  1172.             NSBeginInformationalAlertSheet(LStr(@"Download media?"),
  1173.                                            LStr(@"Yes"),
  1174.                                            LStr(@"No"),
  1175.                                            NULL,
  1176.                                            window,
  1177.                                            self,
  1178.                                            @selector(askDownloadMediaSheetEnded:returnCode:contextInfo:),
  1179.                                            NULL,
  1180.                                            NULL,
  1181. LStr(@"The camera you just plugged in contains %i stored images. Do you want to download them to your computer?"),cameraMediaCount);
  1182.         }
  1183.     }
  1184. }
  1185.  
  1186. - (void) imageReady:(id)cam {
  1187.     if (cam!=driver) return;    //probably an old one
  1188.     [previewView display];
  1189.     imageGrabbed=YES;
  1190.     if (movieRecorder) {    //Movie recording
  1191.         double time=CFAbsoluteTimeGetCurrent();                //What time is it?
  1192.         if (movieRecordStart<=0.0) movieRecordStart=time;        //First image?
  1193.         if ((time-movieLastCapturedImage)>=movieMinCaptureInterval) {    //Minimum capture interval satisfied?
  1194.             [movieRecorder addFrame:imageRep at:(time-movieRecordStart)/movieRecordingTimeFactor];
  1195.             movieLastCapturedImage=time;
  1196.         }
  1197.     }
  1198.     [driver setImageBuffer:[driver imageBuffer] bpp:[driver imageBufferBPP] rowBytes:[driver imageBufferRowBytes]];
  1199. }
  1200.  
  1201. - (void)grabFinished:(id)cam withError:(CameraError)err {
  1202.     [self stopMovieRecording];    //Make sure movie recording is stopped
  1203.     [self setImageOfToolbarItem:PlayToolbarItemIdentifier to:@"PlayToolbarItem"];
  1204.     if (cam!=driver) return;    //probably an old one
  1205.     cameraGrabbing=NO;
  1206.     if (err==CameraErrorOK) [statusText setStringValue:LStr(@"Status: Paused")];
  1207.     else if (err==CameraErrorNoBandwidth) [statusText setStringValue:LStr(@"Status: Not enough bandwidth")];
  1208.     else if (err==CameraErrorNoCam) [statusText setStringValue:LStr(@"Status: Camera unplugged")];
  1209.     else if (err==CameraErrorTimeout) [statusText setStringValue:LStr(@"Status: CPU too busy")];
  1210.     else if (err==CameraErrorUSBProblem) [statusText setStringValue:LStr(@"Status: USB communication problem")];
  1211.     else [statusText setStringValue:LStr(@"Status: Unknown error (this shouldn't happen)")];
  1212.     [fpsPopup setEnabled:YES];
  1213.     [sizePopup setEnabled:YES];
  1214.     [compressionSlider setEnabled:[driver maxCompression]>0];
  1215.     [self updateCameraMediaCount];
  1216. }
  1217.  
  1218. - (void)cameraHasShutDown:(id)cam {
  1219.     [cam release];
  1220.     if (terminating) [[NSApplication sharedApplication] terminate:self]; //Just get me out of here
  1221.     if (cam!=driver) return; //A camera that we have switched away
  1222.     cameraGrabbing=NO;
  1223.     driver=NULL;
  1224.     [statusText setStringValue:LStr(@"Status: No Camera")];
  1225.     [contrastSlider setEnabled:NO];
  1226.     [brightnessSlider setEnabled:NO];
  1227.     [gammaSlider setEnabled:NO];
  1228.     [sharpnessSlider setEnabled:NO];
  1229.     [saturationSlider setEnabled:NO];
  1230.     [manGainCheckbox setEnabled:NO];
  1231.     [gainSlider setEnabled:NO];
  1232.     [shutterSlider setEnabled:NO];
  1233.     [sizePopup setEnabled:NO];
  1234.     [fpsPopup setEnabled:NO];
  1235.     [whiteBalancePopup setEnabled:NO];
  1236.     [compressionSlider setEnabled:NO];
  1237.     [horizontalFlipCheckbox setEnabled:NO];
  1238.     [self updateCameraMediaCount];
  1239.     [inspectorDrawer close];
  1240.     if (inspector) {
  1241.         [inspectorDrawer setContentView:NULL];
  1242.         [inspector release];
  1243.         inspector=NULL;
  1244.     }
  1245. }
  1246.  
  1247. - (void) cameraEventHappened:(id)sender event:(CameraEvent)evt {
  1248.     if (evt==CameraEventSnapshotButtonDown) {
  1249.         [self doSaveImage:self];
  1250.     } else if (evt==CameraEventSnapshotButtonUp) {
  1251.         //Do whatever you want when the button goes up
  1252.     } else NSLog(@"unknown camera event: %i",evt);
  1253. }
  1254.  
  1255. - (BOOL) validateMenuItem:(NSMenuItem *)item {
  1256.     int fps;
  1257.     int res;
  1258.     int wb;
  1259.     if (item==NULL) return NO;
  1260.     fps=([fpsPopup indexOfItem:item]+1)*5;
  1261.     if (fps>0) {        //validate fps entry
  1262.         if (driver==NULL) return NO;    //No camera - no fps
  1263.         else return [driver supportsResolution:[driver resolution] fps:fps];
  1264.     }
  1265.     res=[sizePopup indexOfItem:item]+1;
  1266.     if (res>0) {    //validate res entry
  1267.         if (driver==NULL) return NO;//No camera - no resolution
  1268.         else return [driver supportsResolution:res fps:[driver fps]];
  1269.     }
  1270.     wb=[whiteBalancePopup indexOfItem:item]+1;
  1271.     if (wb>0) {    //validate res entry
  1272.         if (driver==NULL) return NO;//No camera - no white balance
  1273.         else return [driver canSetWhiteBalanceModeTo:wb];
  1274.     }
  1275.     if ([item action]==@selector(doGrab:)) return [self canDoGrab];
  1276.     if ([item action]==@selector(toggleSettings:)) return [self canToggleSettings];
  1277.     if ([item action]==@selector(doSaveImage:)) return [self canDoSaveImage];
  1278.     if ([item action]==@selector(doDeleteAll:)) return [self canDoDeleteAll];
  1279.     if ([item action]==@selector(doDeleteOne:)) return [self canDoDeleteOne];
  1280.     if ([item action]==@selector(doDeleteLast:)) return [self canDoDeleteLast];
  1281.     if ([item action]==@selector(doTakeStillImage:)) return [self canDoTakeStillImage];
  1282.     if ([item action]==@selector(doSavePrefs:)) return [self canDoSavePrefs];
  1283.     if ([item action]==@selector(doDownloadMedia:)) return [self canDoDownloadMedia];
  1284.     if ([item action]==@selector(doNextCam:)) return [self canDoNextCam];
  1285.     return YES;        //Enable every other item
  1286. }    
  1287.  
  1288. //Toolbar stuff
  1289.  
  1290. - (void) setupToolbar {
  1291.     NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: ControllerToolbarIdentifier] autorelease];
  1292.     [toolbar setAllowsUserCustomization: YES];
  1293.     [toolbar setAutosavesConfiguration: YES];
  1294.     [toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
  1295.     [toolbar setDelegate: self];
  1296.     [window setToolbar: toolbar];
  1297. }
  1298.  
  1299. - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
  1300.  
  1301.     NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
  1302.  
  1303.     if ([itemIdent isEqual: PlayToolbarItemIdentifier]) {
  1304.         [toolbarItem setLabel: LStr(@"Play")];
  1305.         [toolbarItem setPaletteLabel: LStr(@"Play")];
  1306.         [toolbarItem setToolTip: LStr(@"Play camera video")];
  1307.         [toolbarItem setImage: [NSImage imageNamed: @"PlayToolbarItem"]];
  1308.         [toolbarItem setTarget: self];
  1309.         [toolbarItem setAction: @selector(doGrab:)];
  1310.     } else if([itemIdent isEqual: SettingsToolbarItemIdentifier]) {
  1311.         [toolbarItem setLabel: LStr(@"Settings")];
  1312.         [toolbarItem setPaletteLabel: LStr(@"Settings")];
  1313.         [toolbarItem setToolTip: LStr(@"Camera video settings")];
  1314.         [toolbarItem setImage: [NSImage imageNamed: @"SettingsToolbarItem"]];
  1315.         [toolbarItem setTarget: self];
  1316.         [toolbarItem setAction: @selector(toggleSettingsDrawer:)];
  1317.     } else if([itemIdent isEqual: DownloadToolbarItemIdentifier]) {
  1318.         [toolbarItem setLabel: LStr(@"Download")];
  1319.         [toolbarItem setPaletteLabel: LStr(@"Download")];
  1320.         [toolbarItem setToolTip: LStr(@"Download media")];
  1321.         [toolbarItem setImage: [NSImage imageNamed: @"DownloadToolbarItem"]];
  1322.         [toolbarItem setTarget: self];
  1323.         [toolbarItem setAction: @selector(doDownloadMedia:)];
  1324.     } else if([itemIdent isEqual: SaveImageToolbarItemIdentifier]) {
  1325.         [toolbarItem setLabel: LStr(@"Save image")];
  1326.         [toolbarItem setPaletteLabel: LStr(@"Save image")];
  1327.         [toolbarItem setToolTip: LStr(@"Save current image")];
  1328.         [toolbarItem setImage: [NSImage imageNamed: @"SnapshotToolbarItem"]];
  1329.         [toolbarItem setTarget: self];
  1330.         [toolbarItem setAction: @selector(doSaveImage:)];
  1331.     } else if([itemIdent isEqual: NextCamToolbarItemIdentifier]) {
  1332.         [toolbarItem setLabel: LStr(@"Change camera")];
  1333.         [toolbarItem setPaletteLabel: LStr(@"Change camera")];
  1334.         [toolbarItem setToolTip: LStr(@"Switch to next camera")];
  1335.         [toolbarItem setImage: [NSImage imageNamed: @"NextCamToolbarItem"]];
  1336.         [toolbarItem setTarget: self];
  1337.         [toolbarItem setAction: @selector(doNextCam:)];
  1338.     } else if([itemIdent isEqual: RecordMovieToolbarItemIdentifier]) {
  1339.         [toolbarItem setLabel: LStr(@"Record movie")];
  1340.         [toolbarItem setPaletteLabel: LStr(@"Record movie")];
  1341.         [toolbarItem setToolTip: LStr(@"Record live video to a QuickTime movie")];
  1342.         [toolbarItem setImage: [NSImage imageNamed: @"RecordMovieToolbarItem"]];
  1343.         [toolbarItem setTarget: self];
  1344.         [toolbarItem setAction: @selector(doRecordMovie:)];
  1345.     } else {
  1346.         toolbarItem = NULL;
  1347.     }
  1348.     return toolbarItem;
  1349. }
  1350.  
  1351. - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar {
  1352.     return [NSArray arrayWithObjects:
  1353.         PlayToolbarItemIdentifier,
  1354.         NSToolbarSpaceItemIdentifier,
  1355.         SaveImageToolbarItemIdentifier,
  1356.         RecordMovieToolbarItemIdentifier,
  1357.         NSToolbarSpaceItemIdentifier,
  1358.         SettingsToolbarItemIdentifier,
  1359.         NULL];
  1360. }
  1361.  
  1362. - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar {
  1363.     return [NSArray arrayWithObjects:
  1364.         PlayToolbarItemIdentifier,
  1365.         SettingsToolbarItemIdentifier,
  1366.         DownloadToolbarItemIdentifier,
  1367.         SaveImageToolbarItemIdentifier,
  1368.         NextCamToolbarItemIdentifier,
  1369.         RecordMovieToolbarItemIdentifier,
  1370.         NSToolbarSpaceItemIdentifier,
  1371.         NSToolbarFlexibleSpaceItemIdentifier,
  1372.         NSToolbarSeparatorItemIdentifier,
  1373.         NULL];
  1374. }
  1375.  
  1376. - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem {
  1377.     BOOL enable = NO;
  1378.     if ([[toolbarItem itemIdentifier] isEqual: PlayToolbarItemIdentifier]) return [self canDoGrab];
  1379.     else if ([[toolbarItem itemIdentifier] isEqual: SettingsToolbarItemIdentifier]) return [self canToggleSettings];
  1380.     else if ([[toolbarItem itemIdentifier] isEqual: DownloadToolbarItemIdentifier]) return [self canDoDownloadMedia];
  1381.     else if ([[toolbarItem itemIdentifier] isEqual: SaveImageToolbarItemIdentifier]) return [self canDoSaveImage];
  1382.     else if ([[toolbarItem itemIdentifier] isEqual: NextCamToolbarItemIdentifier]) return [self canDoNextCam];
  1383.     else if ([[toolbarItem itemIdentifier] isEqual: RecordMovieToolbarItemIdentifier]) return [self canDoRecordMovie];
  1384.     return enable;
  1385. }
  1386.  
  1387. - (BOOL) canDoGrab {
  1388.     return (driver)?YES:NO;
  1389. }
  1390.  
  1391. - (BOOL) canToggleSettings {
  1392.     return YES;
  1393. }
  1394.  
  1395. - (BOOL) canDoDownloadMedia {
  1396.     if (driver) {
  1397.         if (cameraMediaCount>0) {
  1398.             if ([driver canStoreMedia]) {
  1399.                 return (!cameraGrabbing);
  1400.             }
  1401.         }
  1402.     }
  1403.     return NO;
  1404. }
  1405.  
  1406. - (BOOL) canDoSaveImage {
  1407.     return imageGrabbed;
  1408. }
  1409.  
  1410. - (BOOL) canDoNextCam {
  1411.     return ([central numCameras]>1);
  1412. }
  1413.  
  1414. - (BOOL) canDoDeleteAll {
  1415.     if (driver) {
  1416.         if (cameraMediaCount>0) {
  1417.             if ([driver canDeleteAll]) {
  1418.                 return (!cameraGrabbing);
  1419.             }
  1420.         }
  1421.     }
  1422.     return NO;
  1423. }
  1424.  
  1425. - (BOOL) canDoDeleteOne {
  1426.     if (driver) {
  1427.         if (cameraMediaCount>0) {
  1428.             if ([driver canDeleteOne]) {
  1429.                 return (!cameraGrabbing);
  1430.             }
  1431.         }
  1432.     }
  1433.     return NO;
  1434. }
  1435.  
  1436. - (BOOL) canDoDeleteLast {
  1437.     if (driver) {
  1438.         if (cameraMediaCount>0) {
  1439.             if ([driver canDeleteLast]) {
  1440.                 return (!cameraGrabbing);
  1441.             }
  1442.         }
  1443.     }
  1444.     return NO;
  1445. }
  1446.  
  1447. - (BOOL) canDoTakeStillImage {
  1448.     if (driver) {
  1449.         if ([driver canCaptureOne]) {
  1450.             return (!cameraGrabbing);
  1451.         }
  1452.     }
  1453.     return NO;
  1454. }
  1455.  
  1456. - (BOOL) canDoSavePrefs {
  1457.     return (driver!=NULL);
  1458. }
  1459.  
  1460. - (BOOL) canDoRecordMovie {
  1461.     if (driver) {
  1462.         return cameraGrabbing;
  1463.     }
  1464.     return NO;
  1465. }
  1466.  
  1467. - (void) updateCameraMediaCount {
  1468.     cameraMediaCount=0;
  1469.     if (!driver) return;
  1470.     if (![driver canStoreMedia]) return;
  1471.     cameraMediaCount=[driver numberOfStoredMediaObjects];
  1472. }
  1473.  
  1474. - (BOOL)applicationOpenUntitledFile:(NSApplication*)theApplication {
  1475.     [window makeKeyAndOrderFront:self];
  1476.     return YES;
  1477. }
  1478.  
  1479. @end    
  1480.